export function Vue() {
	this._init()
}
/* 
	1.代理data
	2.设置响应式
		1.observe observer
		2.Dep
		3.Watcher
 */
Vue.prototype._init = function (options) {
	this.$options = options
	this.$data = options.data
	this.$el = options.el
	this.$methods = options.methods

	proxy(this.$options, this)
	observe(this.$data)
	compiler()
}

function proxy(options, target) {
	const { data } = options
	// 用this代理data就简单的多，因为get函数中可以直接从data中取值，不用担心触发引用死循环的问题
	Object.keys(data).forEach(key => {
		Object.defineProperty(target, key, {
			get() {
				return data[key]
			},
			set(val) {
				if(val !== data[key]){
					data[key] = val
				}
			},
			Configurable: true,
			Enumerable: true
		})
	})
	return target
}

function isObject(e) {
	return e && typeof e === 'object'
}
function observe(data) {
	// TODO:此处是否就不考虑数组了:是的，简化版代码不考虑数组，其实做数组也很简单，还是调用Object.defineProperty，只不过key是index
	if (!isObject) return data
	Object.keys(data).forEach(key => {
		const dep = new Dep()
		// 这一步作用还蛮大的，一个是避免在get函数中从data中取值触发死循环，二是在此处产生一个闭包缓存(就和dep一样),
		// 后面可以和新set的值进行判断比较。此处这个处理方式应该记下来，对于Object.defineProperty，如果get的时候
		// 需要返回自己的值，那就先把值给缓存下来，get函数中返回缓存的值。如果值有更新，那就在set里面设置缓存的值，
		// 相当于对象中的这个值，就放到缓存了
		let value = data[key]
		if (isObject(value)) {
			observe(value)
		}
		Object.defineProperty(data, key, {
			get() {
				if (Dep.target) {
					dep.depend()
				}
				return value
			},
			set(newVal) {
				if (newVal !== value) {
					// 如果新添的数据是个对象，那给对象也添加上响应式
					data[key] = isObject(newVal) ? observe(newVal) : newVal
					dep.notify()
				}
			},
			Configurable: true,
			Enumerable: true
		})
	})
	return data
}

class Dep {
	constructor() {
		this.subs = []
	}

	depend(watcher) {
		this.subs.push(watcher)
	}

	notify() {
		this.subs.forEach(e => e.update())
	}
}

class Watcher {
	constructor(getter, cb) {
		this.depends = []
		this.getter = getter
		this.cb = cb
	}

	get() {
		Dep.target = this
		this.getter()
	}

	update() {
		const value = this.getter()
		this.cb(value)
	}
}

function compiler(this) {
	const getter = () => { console.log('调取getter函数，从data中取值') }
	const cb = () => { console.log('调取回调函数') }
	this.__ob__ = new Watcher(getter, cb)
}